// websocket工具

import { before } from "node:test";

/**
 * onMessage: 接受消息列表
 */

const heartCheck = {
  timeoutObj: setTimeout(() => {}),
  serverTimeoutObj: setInterval(() => {}),
  reset: function () {
    clearTimeout(this.timeoutObj);
    clearTimeout(this.serverTimeoutObj);
    return this;
  },
  start: function (socket, timeOut) {
    var self = this;
    clearTimeout(this.timeoutObj);
    clearTimeout(this.serverTimeoutObj);
    this.timeoutObj = setTimeout(function () {
      //这里发送一个心跳，后端收到后，返回一个心跳消息，
      //onmessage拿到返回的心跳就说明连接正常
      socket.send(
        JSON.stringify({
          action: "ping"
        })
      );
      console.log("ping");
      self.serverTimeoutObj = setTimeout(function () {
        // 如果超过一定时间还没重置，说明后端主动断开了
        console.log("关闭服务");
        socket.close(); //如果onclose会执行reconnect，我们执行 websocket.close()就行了.如果直接执行 reconnect 会触发onclose导致重连两次
      }, timeOut);
    }, timeOut);
  }
};

class WSUtil {
  private static instance: WSUtil = new WSUtil();
  private static socket: WebSocket;
  private static options: {
    onMessage: Function;
    wsUrl: string;
    timeOut?: number;
    lockReconnect?: boolean;
  };
  private timer: ReturnType<typeof setTimeout>;

  private constructor() {}
  public static me() {
    return WSUtil.instance;
  }

  /**
   * 发送
   * @param message
   */
  public send(message: any) {
    if (WSUtil.socket && WSUtil.socket.readyState == 1) {
      WSUtil.socket.send(message);
    }
  }
  private reconnect = () => {
    if (WSUtil.options.lockReconnect) return;
    WSUtil.options.lockReconnect = true;
    clearTimeout(this.timer);
    this.timer = setTimeout(() => {
      this.start(null);
    }, 4000);
  };

  public start(options: {
    onMessage: Function;
    wsUrl: string;
    timeOut: number;
    lockReconnect?: boolean;
  }) {
    let that = this;
    if (options) {
      WSUtil.options = options;
      WSUtil.options.timeOut = options.timeOut ?? 5000;
      WSUtil.options.lockReconnect = options.lockReconnect ?? false;
    }

    if (!WSUtil.options.wsUrl) {
      throw new Error("ws:链接地址不存在!");
    }
    try {
      this.beforeStart();
    } catch (error) {
      console.log("链接websocket失败:", error);
    }
  }

  beforeStart() {
    let that = this;
    WSUtil.socket = new WebSocket(WSUtil.options.wsUrl);
    WSUtil.socket.onopen = function (event) {
      console.log("WebSocket:已连接");
      //心跳检测重置
      heartCheck.reset().start(WSUtil.socket, WSUtil.options.timeOut);
    };

    //接收到消息的回调方法
    WSUtil.socket.onmessage = function (event) {
      console.log("WebSocket:收到一条消息", event.data);
      const isHeart = /pong/.test(event.data);
      if (WSUtil.options.onMessage && !isHeart) {
        // 触发自定义onMessage
        WSUtil.options.onMessage.call(null, event);
      }
      heartCheck.reset().start(WSUtil.socket, WSUtil.options.timeOut);
    };

    //连接发生错误的回调方法
    WSUtil.socket.onerror = function (event) {
      console.log("WebSocket:发生错误");
      that.reconnect();
    };

    //连接关闭的回调方法
    WSUtil.socket.onclose = function (event) {
      console.log("WebSocket:已关闭");
      heartCheck.reset(); //心跳检测
      that.reconnect();
    };

    //监听窗口关闭事件，当窗口关闭时，主动去关闭websocket连接，防止连接还没断开就关闭窗口，server端会抛异常。
    window.onbeforeunload = function () {
      WSUtil.socket.close();
    };
  }
}

export default WSUtil;
